Skip to content

v0.6.51: tables improvements, billing fixes, 404 pages, code hygiene#4233

Open
waleedlatif1 wants to merge 9 commits intomainfrom
staging
Open

v0.6.51: tables improvements, billing fixes, 404 pages, code hygiene#4233
waleedlatif1 wants to merge 9 commits intomainfrom
staging

Conversation

@waleedlatif1
Copy link
Copy Markdown
Collaborator

waleedlatif1 and others added 9 commits April 18, 2026 01:30
… undo improvements (#4222)

* feat(tables): add column selection, missing keyboard shortcuts, and Sheets-aligned operations

Click column headers to select entire columns, shift-click to extend to
a column range. Delete, cut, and copy operations work on column
selections with full undo/redo support. Adds Home, End, Ctrl+Home,
Ctrl+End, PageUp, PageDown, Ctrl+Space, and all Shift variants.
Changes Ctrl+A to select all cells instead of checkbox rows. Column
header dropdown menu now opens on right-click instead of left-click.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(tables): chevron opens dropdown, drag header to reorder columns

Split column header into label area (click to select, draggable for
reorder) and chevron button (click to open dropdown menu). Remove
the grip handle — dragging the header itself now reorders columns.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(tables): full-column highlight during drag reorder

Replace the thin 2px line drop indicator with a full-column highlight
that spans the entire table height, matching Google Sheets behavior.
The insertion line is still shown at the drop edge for precision.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(tables): handle drag reorder edge cases, dim source column

Suppress drop indicator when drag would result in no position change
(dragging onto self or adjacent no-op positions). Dim the source
column body cells during drag with a background overlay. Skip the
API call when the computed order is identical to the current order.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* feat(tables): add column reorder undo/redo, body drop targets, and escape cancel

Column drag-and-drop now supports dropping anywhere in a column (not just headers),
pressing Escape to cancel a drag, and full undo/redo integration for column reordering.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(tables): merge partial updates in updateRow to prevent column data loss

When Mothership called updateRow directly (bypassing the PATCH API route),
it passed only the changed fields — which were written as the entire row,
wiping all other columns. Move the merge logic into updateRow itself so
all callers get correct partial-update semantics, and remove the now-redundant
pre-merge from both PATCH routes.

* test(tables): add updateRow partial merge tests

Covers the bug where partial updates wiped unmentioned columns — verifies
that fields not in the update payload are preserved, nulling a field works,
full-row updates are idempotent, and missing rows throw correctly.

* feat(tables): add delete-column undo/redo, rename metadata sync, and comprehensive row ID patching

- Delete column now captures column definition, cell data, order, and width for full undo/redo
- Column rename undo/redo now properly syncs columnWidths and columnOrder metadata
- patchRedoRowId/patchUndoRowId extended to handle all action types containing row IDs

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(tables): remove source column dimming during drag reorder

Only show the insertion line at the drop position, matching Google Sheets
behavior. Remove dragSourceBounds memo and isDragging prop.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(tables): preserve selection on right-click, auto-resize on double-click, fix escape during drag

- Right-clicking within an existing selection now preserves it instead of
  resetting to a single cell, so context menu operations apply to the full range
- Double-clicking a column border auto-resizes the column to fit its content
- Escape during column drag now immediately clears refs before state update,
  preventing the dragend handler from executing the reorder

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(tables): add aria-hidden value and aria-label for column header accessibility

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(tables): tighten auto-resize padding to match Google Sheets

Reduce header padding from +48px to +36px (icon + cell padding) and cell
padding from +20px to +17px (cell padding + border) for a snug fit.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(tables): clean drag ghost and clear selection on drag start

- Create a minimal custom drag image showing only the column name instead
  of the browser's default ghost that includes adjacent columns/checkboxes
- Clear any existing cell/column selection when starting a column drag to
  prevent stale highlights from persisting during reorder

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* feat(tables): add Shift+Space row selection and Ctrl+D fill down

Shift+Space now selects the entire row (all columns) instead of toggling
a checkbox, matching Google Sheets behavior. Ctrl+D copies the top cell's
value down through the selected range with full undo/redo support.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(tables): show toast on incompatible column type change

The server validates type compatibility and returns a clear error message
(e.g. "3 row(s) have incompatible values"), but the client was silently
swallowing it. Now surfaces the error via toast notification. Also moved
the undo push to onSuccess so a failed type change doesn't pollute the
undo stack.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(tables): scroll-into-view for selection focus, Home/End origin, delete-column undo timing

- Scroll-into-view now tracks selectionFocus (not just anchor), so
  Shift+Arrow extending selection off-screen properly auto-scrolls
- Shift+Home/End now uses the current focus as origin (matching
  Shift+Arrow behavior) instead of always using anchor
- Delete column undo entry is now pushed in onSuccess, preventing
  a corrupted undo stack if the server rejects the deletion
- Dialog copy updated from "cannot be undone" to "You can undo this
  action" since undo/redo is supported

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: resolve duplicate declarations from rebase against staging

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix file upload

* fix(tables): merge column widths on delete-column undo, try/finally for auto-resize

- Delete-column undo now reads current column widths via getColumnWidths
  callback and merges the restored column's width into the full map,
  preventing other columns' widths from being wiped
- Auto-resize measurement span is now wrapped in try/finally to ensure
  DOM cleanup if an exception occurs during measurement

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: revert accidental home.tsx change from rebase conflict resolution

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(tables): clear isColumnSelection on double-click and right-click, skip scroll for column select

- Clear isColumnSelection when double-clicking a cell to edit, preventing
  the column selection effect from fighting with the editing state
- Clear isColumnSelection when right-clicking outside the current
  selection, preventing stale column selection from re-expanding
- Skip scroll-into-view when isColumnSelection is true, preventing
  the viewport from jumping to the bottom row when clicking a column header

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(tables): remove inline font override in auto-resize, guard undefined columnOrder

- Remove `font:inherit` from measurement span inline style so Tailwind
  classes (font-medium, text-small) control font properties for accurate
  column width measurement
- Only include columnOrder in metadata update when defined, preventing
  handleColumnRename from clearing a persisted column order when
  columnOrderRef is null

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(tables): capture columnRequired in delete-column undo for full restoration

The delete-column undo action captured columnUnique but not columnRequired,
so undoing a delete on a required column would silently drop the constraint.
Now captures and restores both constraints.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(tables): restore width independently of order on delete-column undo, batch fill-down

- Column width restoration in delete-column undo no longer requires
  previousOrder to be non-null — width is restored independently
- Ctrl+D fill-down now uses batchUpdateRef (single API call) instead
  of calling mutateRef per row in a loop

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(tables): multi-column delete, select-all cell model, cut flash, chevron alignment

- Multi-select delete: detect column selection range and delete all selected
  columns sequentially with individual undo entries
- Select all (header checkbox): use cell selection model instead of checkbox
  model for consistent highlighting
- Cut flash: batch cell clears into single mutation to prevent stale data
  flashing from multiple onSettled invalidations
- Chevron alignment: adjust right padding from pr-2 to pr-2.5

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(tables): restore column width locally on delete-column undo

Add onColumnWidthsChange callback to undo hook so restored column
widths update local component state, not just server metadata.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(tables): prevent Ctrl+D bookmark dialog, batch Delete/Backspace mutations

- Move e.preventDefault() before early returns in Ctrl+D handler so
  the browser bookmark dialog is always suppressed
- Replace per-row mutateRef calls with single batchUpdateRef call in
  both Delete/Backspace handlers (checked rows and cell selection),
  consistent with cut and fill-down

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(tables): adjust column positions for multi-column delete undo

Capture original schema positions upfront and adjust each by the
count of previously-deleted columns with lower positions, so undo
restores columns at correct server-side positions in LIFO order.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(tables): only multi-delete when clicked column is within selection

Check that the right-clicked column is within the selected column
range before using multi-column delete. If the click is outside the
selection, delete only the clicked column.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(tables): prevent duplicate undo entry on column drag-drop

Clear dragColumnNameRef immediately in handleColumnDragEnd so the
second invocation (from dragend after drop already fired) is a no-op.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(tables): clean up width on delete-column redo, suppress click during drag

- Redo path for delete-column now removes the column's width from
  metadata and local state, preventing stale width entries
- Add didDragRef to ColumnHeaderMenu to suppress the click event
  that fires after a drag operation, preventing selection flash

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(tables): remove unstable mutation object from useCallback deps

deleteTableMutation is not referentially stable — only .mutateAsync()
is. Including the mutation object causes unnecessary callback recreation
on every mutation state change.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(tables): fix auto-resize header padding, deduplicate rename metadata logic

Increase header text measurement padding from 36px to 57px to account
for the chevron dropdown button (pl-0.5 + 9px icon + pr-2.5) that
always occupies layout space. Prevents header text truncation on
auto-resize.

Deduplicate column rename metadata logic by having columnRename.onSave
call handleColumnRename instead of reimplementing the same width/order
transfer and metadata persist.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(tables): log error on cell data restoration failure during undo

Add onError handler to the batchUpdateRowsMutation inside
delete-column undo so failures are logged instead of silently
swallowed. The column schema restores first, and the cell data
restoration is a separate async call that the outer try/catch
cannot intercept.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(tables): address audit findings across table, undo hook, and store

- Add missing bounds check in handleCopy (c >= cols.length) matching
  handleCut for defensive consistency
- Clear lastCheckboxRowRef in Ctrl+Space and Shift+Space to prevent
  stale shift-click checkbox range after keyboard selection
- Fix stale snapshot race in patchRedoRowId/patchUndoRowId by reading
  state inside the set() updater instead of via get() outside it
- Add metadata cleanup to create-column undo so column width is removed
  from both local state and server, symmetric with delete-column redo
- Remove stale width key from columnWidths on column delete instead of
  persisting orphaned entries
- Normalize undefined vs null in handleInlineSave change detection to
  prevent unnecessary mutations when oldValue is undefined
- Use ghost.parentNode?.removeChild instead of document.body.removeChild
  in drag ghost cleanup to prevent throw on component unmount

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(tables): reset didDragRef in handleDragEnd to prevent stale flag

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
* chore(readme): update tech stack section

* fix
…4226)

* fix(blocks): resolve variable display in mothership resource preview

Variables block showed empty assignments in the embedded workflow preview
because currentWorkflowId was read from URL params, which don't contain
workflowId in the mothership route. Fall back to activeWorkflowId from
the workflow registry.

* fix(blocks): narrow currentWorkflowId to string to satisfy strict null checks
…lan from Stripe, transfer storage on org join, outbox service (#4219)

* fix(billing): route scope by subscription referenceId, sync plan from Stripe, transfer storage on org join

Route every billing decision (usage limits, credits, storage, rate
limit, threshold billing, webhooks, UI permissions) through the
subscription's `referenceId` instead of plan-name heuristics. Fixes
the production state where a `pro_6000` subscription attached to an
organization was treated as personal Pro by display/edit code while
execution correctly enforced the org cap.

Scope
- Add `isOrgScopedSubscription(sub, userId)` (pure) and
  `isSubscriptionOrgScoped(sub)` (async DB-backed) helpers. One is
  used wherever a user perspective is available; the other in webhook
  handlers that only have a subscription row.
- Replace plan-name scope checks in ~20 files: usage/limit readers,
  credits balance + purchase, threshold billing, storage limits +
  tracking, rate limiter, invoice + subscription webhooks, seat
  management, membership join/leave, `switch-plan` admin gate,
  admin credits/billing routes, copilot 402 handler, UI subscription
  settings + permissions + sidebar indicator, React Query types.

Plan sync
- Add `syncSubscriptionPlan(subscriptionId, currentPlan, planFromStripe)`
  called from `onSubscriptionComplete` and `onSubscriptionUpdate` so
  the DB `plan` column heals on every Stripe event. Pro->Team upgrades
  previously updated price, seats, and referenceId but left `plan`
  stale — this is what produced the `pro_6000`-on-org row.

Priority + grace period
- `getHighestPrioritySubscription` now prefers org over personal
  within each tier (Enterprise > Team > Pro, org > personal at each).
  A user with a `cancelAtPeriodEnd` personal Pro who joins a paid org
  routes pooled resources to the org through the grace window.
- `calculateSubscriptionOverage` personal-Pro branch reads user_stats
  directly (bypassing priority) and bills only `proPeriodCostSnapshot`
  when the user joined a paid org mid-cycle, so post-join org usage
  isn't double-charged on the personal Pro's final invoice.
  `resetUsageForSubscription` mirrors this: preserves
  `currentPeriodCost` / `currentPeriodCopilotCost` when
  `proPeriodCostSnapshot > 0` so the org's next cycle-close captures
  post-join usage correctly.

Uniform base-price formula
- `basePrice × (seats ?? 1)` everywhere: `getOrgUsageLimit`,
  `updateOrganizationUsageLimit`, `setUsageLimitForCredits`,
  `calculateSubscriptionOverage`, threshold billing,
  `syncSubscriptionUsageLimits`, `getOrganizationBillingData`.
  Admin dashboard math now agrees with enforcement math.

Storage transfer on join
- Invitation-accept flow moves `user_stats.storageUsedBytes` into
  `organization.storageUsedBytes` inside the same transaction when
  the org is paid.
- `syncSubscriptionUsageLimits` runs a bulk-backfill version so
  members who joined before this fix, or orgs that upgraded from
  free to paid after members joined, get pulled into the org pool
  on the next subscription event. Idempotent.

UX polish
- Copilot 402 handler differentiates personal-scoped ("increase your
  usage limit") from org-scoped ("ask an owner or admin to raise the
  limit") while keeping the `increase_limit` action code the parser
  already understands.
- Duplicate-subscription error on team upgrade names the existing
  plan via `getDisplayPlanName`.
- Invitation-accept invalidates subscription + organization React
  Query caches before redirect so settings doesn't flash the user's
  pre-join personal view.

Dead code removal
- Remove unused `calculateUserOverage`, and the following fields on
  `SubscriptionBillingData` / `getSimplifiedBillingSummary` that no
  consumer in the monorepo read: `basePrice`, `overageAmount`,
  `totalProjected`, `tierCredits`, `basePriceCredits`,
  `currentUsageCredits`, `overageAmountCredits`, `totalProjectedCredits`,
  `usageLimitCredits`, `currentCredits`, `limitCredits`,
  `lastPeriodCostCredits`, `lastPeriodCopilotCostCredits`,
  `copilotCostCredits`, and the `organizationData` subobject. Add
  `metadata: unknown` to match what the server returns.

Notes for the triggering customer
- The `pro_6000`-on-org row self-heals on the next Stripe event via
  `syncSubscriptionPlan`. For the one known customer, a direct
  UPDATE is sufficient:
  `UPDATE subscription SET plan='team_6000' WHERE id='aq2...' AND plan='pro_6000'`.

Made-with: Cursor

* fix tests

* address more comments

* progress

* harden further

* outbox service

* address comments

* address comment on check

* simplify

* cleanup code

* minor improvement
)

* fix(ui): stop scrolling on leaving workflow sidebar for drag-drop

* Address comments, fix hover state

* address comments
…ove dead code (#4228)

* improvement(codebase): centralize test mocks, extract @sim/utils, remove dead code

* improvement(codebase): apply @sim/utils conventions to staging-introduced files
)

* chore(docker): add packages/utils to app and realtime Dockerfiles

* chore(docker): copy packages/utils in realtime runner stage
…eanup hooks and design tokens (#4231)

* fix(settings): restore paste-to-destructure for workspace secrets, cleanup hooks and design tokens

Restores the env-var paste feature (KEY=VALUE → split rows, multi-line
→ multi rows) for workspace secrets that was lost when the unified
Credentials tab was split into Secrets and Integrations. Adds
`parseEnvVarLine`, `parseValidEnvVars`, and `handleWorkspacePaste` with
full support for export prefix, quoted values, inline comments, and
base64 false-positive guards. Also adds consistent value masking
(show on focus / mask on blur) to new workspace input rows.

Cleans up ~20 unnecessary `useCallback` wrappers, fixes a direct state
mutation in `handleSingleValuePaste`, moves `e.preventDefault()` inside
the `parsedVars.length > 0` guard, replaces all hardcoded hex colors
with CSS variable tokens, converts template-literal classNames to `cn()`,
and replaces raw `<button>` with emcn `Button`.

* fix(settings): fix handlePaste silent swallow, quote-strip bug, and credential sync efficiency

- Move e.preventDefault() inside parsedVars guard in handlePaste so KEY= lines
  don't silently discard input (mirrors handleWorkspacePaste fix from same PR)
- Add value.length >= 2 guard before quote-stripping to prevent single-char
  values like KEY=\" from being stripped to empty and silently dropped
- Introduce createWorkspaceEnvCredentials and deleteWorkspaceEnvCredentials
  for delta-aware credential sync (O(k) instead of O(n*m) for env var mutations)
- Fix createWorkspaceEnvCredentials early-return bug that skipped credential
  record creation when workspace had zero members
- Update credentials/[id] DELETE to use deleteWorkspaceEnvCredentials instead
  of full syncWorkspaceEnvCredentials
- Optimize syncWorkspaceEnvCredentials to fetch workspace+member IDs in parallel
  once instead of once per credential

* fix(settings): normalize Windows line endings in paste handlers

* fix(settings): eliminate double-parse in handlePaste by inlining handleKeyValuePaste
@vercel
Copy link
Copy Markdown

vercel bot commented Apr 19, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Actions Updated (UTC)
docs Skipped Skipped Apr 19, 2026 4:23am

Request Review

@cursor
Copy link
Copy Markdown

cursor bot commented Apr 19, 2026

PR Summary

Medium Risk
Medium risk because it changes billing API authorization/scope logic and alters the shape/content of billing responses, which could impact client behavior and access control. Most other changes are doc/test refactors and import migrations with low runtime risk.

Overview
Utility + hygiene updates: Standardizes ID/error/sleep helpers to @sim/utils (replacing @/lib/core/utils/*) across multiple API routes and updates agent guidelines/docs accordingly.

Testing refactor: Reworks many API route tests to use centralized module mocks from @sim/testing instead of local vi.hoisted() mock boilerplate.

Product/runtime changes: Adds custom not-found.tsx pages for /models and /integrations landing routes. Updates billing APIs to (1) enforce org membership when requesting user-context billing with an org contextId, (2) remove credit-converted fields from the org billing response payload, and (3) gate plan switching admin checks using isOrgScopedSubscription() rather than plan-name heuristics.

Reviewed by Cursor Bugbot for commit 1ced54a. Configure here.

@waleedlatif1
Copy link
Copy Markdown
Collaborator Author

@greptile

@waleedlatif1
Copy link
Copy Markdown
Collaborator Author

@cursor review

Copy link
Copy Markdown

@cursor cursor bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 2 potential issues.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 1ced54a. Configure here.

Comment thread apps/sim/app/(landing)/integrations/not-found.tsx
Comment thread apps/sim/app/(landing)/integrations/not-found.tsx
Copy link
Copy Markdown

@cursor cursor bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 1ced54a. Configure here.

Comment thread apps/sim/app/api/chat/[identifier]/otp/route.test.ts
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants